//********************************************************************************************
//
// File : ip.cpp implement for Internet Protocol
//
//********************************************************************************************

#include "StrLib.h"
#include "ip.h"
#include "Config.h"
//********************************************************************************************
//
// +------------+-----------+----------+
// + MAC header + IP header + Data ::: +
// +------------+-----------+----------+
//
// IP Header
//
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +00+01+02+03+04+05+06+07+08+09+10+11+12+13+14+15+16+17+18+19+20+21+22+23+24+25+26+27+28+29+30+31+
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// + Version   +   IHL     +             TOS       +                Total length                   +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +                  Identification               +  Flags +          Fragment offset             +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +          TTL          +       Protocol        +                Header checksum                +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +                                      Source IP address                                        +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +                                    Destination IP address                                     +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +                                    Options and padding :::                                    +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
//
//********************************************************************************************
U16 cIP::IdentifyCnt = 1;
U8  cIP::s_pMacAddr[MAC_ADDR_LEN];
U8  cIP::s_pIPAddr[IP_ADDR_LEN];
U16 cIP::s_UDPSrcPort;
U16 cIP::s_TCPSrcPort;


void cIP::InitReset()
{
    s_pMacAddr[0] = cConfig::DEFAULT_MAC_ADDR_0;
    s_pMacAddr[1] = cConfig::DEFAULT_MAC_ADDR_1;
    s_pMacAddr[2] = cConfig::DEFAULT_MAC_ADDR_2;
    s_pMacAddr[3] = cConfig::DEFAULT_MAC_ADDR_3;
    s_pMacAddr[4] = cConfig::DEFAULT_MAC_ADDR_4;
    s_pMacAddr[5] = cConfig::DEFAULT_MAC_ADDR_5;
    cConfig::GetIPConfig(s_pIPAddr, s_TCPSrcPort);
    s_UDPSrcPort = 6789;
}


void cIP::GenerateMacHeader ( U8 *pBuff, U16 iType, U8 *iDestMac )
{
    cStrLib::Strcpy(&pBuff[MAC_DST_MAC_POS], iDestMac, MAC_ADDR_LEN);
    cStrLib::Strcpy(&pBuff[MAC_SRC_MAC_POS], s_pMacAddr, MAC_ADDR_LEN);
    pBuff[ MAC_TYPE_H_POS ] = cStrLib::GetHigh16( iType );
    pBuff[ MAC_TYPE_L_POS ] = cStrLib::GetLow16( iType );
}

//********************************************************************************************
//
// Function : ip_generate_packet
// Description : generate all ip header
//
//********************************************************************************************
void cIP::GenerateIpHeader ( U8 *pTxBuff, U16 iTotalLength, U8 iProtocol, U8 *DestIP )
{
    // set ipv4 and header length
    pTxBuff[ IP_P ] = IP_V4_V | IP_HEADER_LENGTH_V;

    // set TOS to default 0x00
    pTxBuff[ IP_TOS_P ] = 0x00;

    // set total length
    pTxBuff [ IP_TOTLEN_H_P ] = cStrLib::GetHigh16( iTotalLength );
    pTxBuff [ IP_TOTLEN_L_P ] = cStrLib::GetLow16( iTotalLength );

    // set packet identification
    pTxBuff [ IP_ID_H_P ] = cStrLib::GetHigh16( IdentifyCnt );
    pTxBuff [ IP_ID_L_P ] = cStrLib::GetLow16( IdentifyCnt );
    IdentifyCnt++;

    // set fragment flags	
    pTxBuff [ IP_FLAGS_H_P ] = 0x00;
    pTxBuff [ IP_FLAGS_L_P ] = 0x00;

    // set Time To Live
    pTxBuff [ IP_TTL_P ] = 128;

    // set ip packet type to tcp/udp/icmp...
    pTxBuff [ IP_PROTO_P ] = iProtocol;

    // copy source and destination ip address
    cStrLib::Strcpy(&pTxBuff[ IP_DST_IP_P ], DestIP, 4);
    cStrLib::Strcpy(&pTxBuff[ IP_SRC_IP_P ], s_pIPAddr, 4);

    // clear the 2 byte checksum
    pTxBuff[ IP_CHECKSUM_H_P ] = 0;
    pTxBuff[ IP_CHECKSUM_L_P ] = 0;

    // fill checksum value
    // calculate the checksum:
    U16 temp_U16 = SoftwareChecksum( &pTxBuff[ IP_P ], IP_HEADER_LEN, 0 );
    pTxBuff[ IP_CHECKSUM_H_P ] = cStrLib::GetHigh16( temp_U16 );
    pTxBuff[ IP_CHECKSUM_L_P ] = cStrLib::GetLow16( temp_U16 );
}
//********************************************************************************************
//
// Function : ip_check_ip
// Description : Check incoming packet
//
//********************************************************************************************
bool cIP::IsValidPacket ( U8 *pBuff )
{
    // if ethernet type is not ip
    if ( pBuff[ MAC_TYPE_H_POS ] != MAC_TYPE_IP_H_VAL || pBuff[ MAC_TYPE_L_POS ] != MAC_TYPE_IP_L_VAL)
        return (false);

    // if UDP packet then do not check dest IP
    if ( pBuff [ IP_PROTO_P ] == IP_PROTO_UDP_V )
    {
        return (true);
    }

    // if ip packet not send to device
    return (cStrLib::Strcmp (&pBuff[ IP_DST_IP_P ], s_pIPAddr, IP_ADDR_LEN));
}

U16 cIP::SoftwareChecksum(U8 *pBuff, U16 iLen, U32 iSum)
{
    // build the sum of 16bit words
    while(iLen>1)
    {
        iSum += 0xFFFF & (*pBuff<<8|*(pBuff+1));
        pBuff+=2;
        iLen-=2;
    }
    // if there is a byte left then add it (padded with zero)
    if (iLen)
    {
        iSum += (0xFF & *pBuff)<<8;
    }
    // now calculate the sum over the bytes in the sum
    // until the result is only 16bit long
    while (iSum>>16)
    {
        iSum = (iSum & 0xFFFF)+(iSum >> 16);
    }
    // build 1's complement:
    return( (U16) iSum ^ 0xFFFF);
}


bool cIP::MACCmp(U8 *pMAC)
{
    for (U8 i=0; i<MAC_ADDR_LEN; i++)
    {
        if (pMAC[i] != s_pMacAddr[i]) return (false);
    }
    return (true);
}


bool cIP::IPCmp(U8 *pIP)
{
    for (U8 i=0; i<IP_ADDR_LEN; i++)
    {
        if (pIP[i] != s_pIPAddr[i]) return (false);
    }
    return (true);
}


bool cIP::UDPPortCmp(U8 *pPort)
{
    if ( ((((U16)pPort[0])<<8) | pPort[1]) != s_UDPSrcPort ) 
    {
        return (false);
    }
    else
    {
        return (true);
    }
}

bool cIP::TCPPortCmp(U8 *pPort)
{
    if ( ((((U16)pPort[0])<<8) | pPort[1]) != s_TCPSrcPort ) 
    {
        return (false);
    }
    else
    {
        return (true);
    }
}

void cIP::ChangeIPAndPort(U8* pBuff)
{
    cStrLib::Strcpy(s_pIPAddr, pBuff, IP_ADDR_LEN);
    s_TCPSrcPort = cStrLib::StrToWord(&pBuff[IP_ADDR_LEN]);
    cConfig::SetIPConfig(s_pIPAddr, s_TCPSrcPort);
    
}


